const db = wx.cloud.database();

const cache = {};

/**
 * Shall only be invoked AFTER `wx.cloud.init`.
 * Exceptions might be thrown if the cloud environment is not initialized.
 * 
 * @returns {Promise<String>} The OpenId. Reject on server error.
 */
const refreshOpenId = () => {
  return new Promise((resolve, reject) => {
    wx.cloud.callFunction({
      name: 'login',
      success: (res) => {
        if (res.result) {
          // good
          console.log('Obtained OpenId', res.result.openid);
          cache['openid'] = res.result.openid;
          resolve(res.result.openid);
        }
      },
      fail: (err) => {
        console.log('Server error?');
        console.log(err);
        reject(err);
      }
    });
  });
};

const userInfoGrab = () =>{
  return new Promise((resolve, reject) => {
    wx.getUserInfo({
      success: (res) => {
        resolve(res.userInfo);
      },
      fail: (e) => {
        reject(e);
      }
    });
  });
};

/**
 * Get user info. Ask for permission first and do the job.
 * 
 * If the user denied access, the promise will be rejected without a reason.
 * The reason will be provided otherwise.
 * 
 * @returns {Promise<UserInfo>} User info
 */
const refreshUserInfo = () => {
  return new Promise((resolve, reject) => {
    wx.getSetting({
      success: (res) => {
        if (!res.authSetting['scope.userInfo']) {
          console.log('authorizing')
          wx.authorize({
            scope: 'scope.userInfo',
            success: () => {
              userInfoGrab().then(res => {
                cache['userInfo'] = res;
                resolve(res);
              }).catch(e => reject(e));
            },
            fail: () => {
              reject();
            }
          });
        } else {
          userInfoGrab().then(res => {
            cache['userInfo'] = res;
            resolve(res);
          }).catch(e => reject(e));
        }
      },
      fail: (e) => {
        reject(e);
      }
    });
  });
};

/**
 * Gets user info. The cache will be used if present.
 * 
 * @returns {Promise<UserInfo>} User info
 */
const getUserInfo = () => {
  return new Promise((resolve, reject) => {
    if (cache['userInfo'] != null) {
      resolve(cache['userInfo']);
    } else {
      refreshUserInfo().then(info => resolve(info)).catch(e => reject(e));
    }
  });
};

/**
 * Gets the OpenId of the user. User does NOT have to 
 * login to get the ID.
 * 
 * Promise would be rejected if there is a connection error.
 * 
 * @returns {Promise<string>} OpenId
 */
const getOpenId = () => {
  return new Promise((resolve, reject) => {
    if (cache['openid'] != null) {
      resolve(cache['openid']);
    } else {
      refreshOpenId().then(id => resolve(id)).catch(e => reject(e));
    }
  });
};

/**
 * Test if user has already authorized the app to use UserInfo.
 * 
 * @returns {boolean} literal
 */
const isAuthorized = () => {
  return cache.hasOwnProperty('userInfo');
};

/**
 * Get user info from cache.
 * 
 * If user didn't authorize first, undefined will be returned.
 * 
 * @returns {UserInfo} info
 */
const getCachedUserInfo = () => {
  return cache['userInfo'];
};

const isUserAuthorized = () => {
  return new Promise((resolve, reject) => {
    wx.getSetting({
      success: (res) => {
        resolve(res.authSetting['scope.userInfo']);
      },

      fail: function (e) {
        reject(e);
      }
    })
  });
};

/**
 * 
 */
const refreshUserDetails = async () => {
  const id = await getOpenId();
  const result = await db.collection('users').where({
    openid: db.command.eq(id)
  }).get();
  cache['userDetails'] = result.data[0];
  return result.data[0];
};

/**
 * 
 */
const getUserDetails = async () => {
  if (cache.hasOwnProperty('userDetails')) {
    return cache['userDetails'];
  } else {
    return await refreshUserDetails();
  }
};

/**
 * Get user details from cache.
 */
const getCachedUserDetails = () => {
  return cache['userDetails'];
};

/**
 * Test if user has registered.
 */
const isUserRegistered = async () => {
  const openid = await getOpenId();
  const count = await db.collection('users').where({
    openid: db.command.eq(openid)
  }).count();
  return count.total > 0;
};

/**
 * 
 */
const writeOrUpdateUserDetail = async (data) => {
  const registered = await isUserRegistered();
  if (registered) {
    // update
    await db.collection('users').doc(cache['openid']).update({
      data: data
    })
  } else {
    // new
    data['openid'] = cache['openid'];
    data['maxBorrowCount'] = 10;
    data['credit'] = 100;
    await db.collection('users').add({
      data: data
    })
  }
};

module.exports = {
  refreshOpenId: refreshOpenId,
  refreshUserInfo: refreshUserInfo,
  getUserInfo: getUserInfo,
  getOpenId: getOpenId,
  isAuthorized: isAuthorized,
  getCachedUserInfo: getCachedUserInfo,
  isUserAuthorized: isUserAuthorized,
  getUserDetails: getUserDetails,
  isUserRegistered: isUserRegistered,
  writeOrUpdateUserDetail: writeOrUpdateUserDetail,
};
